home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / MPW / fgrep 1.1 / fgrep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-10  |  14.1 KB  |  691 lines  |  [TEXT/MPS ]

  1. /* fgrep.c - grep program built around matcher.
  2.    Copyright 1989 Free Software Foundation
  3.           Written August 1989 by Mike Haertel.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 1, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.    The author may be reached (Email) at the address mike@ai.mit.edu,
  20.    or (US mail) as Mike Haertel c/o Free Software Foundation. */
  21.  
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25.  
  26. #include <stddef.h>
  27. #include <limits.h>
  28. #include <errno.h>
  29. #include <stdlib.h>
  30. #include <fcntl.h>
  31. #include "getopt.h"
  32.  
  33. extern void error (int status, int errnum, const char *mesg, ...);
  34. #define FGREP_ERROR(mesg, errnum)    error(0, errnum, mesg), error_seen = 1
  35. #define FATAL(mesg, errnum)        error(2, errnum, mesg)
  36.  
  37. /* Deal with <ctype.h> lossage. */
  38. #include <ctype.h>
  39.  
  40. #ifdef macintosh
  41. #ifndef isascii
  42. #define isascii(c)        ((unsigned char)(c)<=0177)
  43. #endif
  44. #endif
  45.  
  46. #ifndef isascii
  47. #define ISALNUM(C) isalnum(C)
  48. #define ISUPPER(C) isupper(C)
  49. #define TOLOWER(C) tolower(C)
  50. #else
  51. #define ISALNUM(C) (isascii(C) && isalnum(C))
  52. #define ISUPPER(C) (isascii(C) && isupper(C))
  53. #define TOLOWER(C) (ISUPPER(C) ? tolower(C) : (C))
  54. #endif
  55.  
  56. typedef void *PTR;
  57.  
  58. extern PTR xmalloc ();
  59. extern PTR xrealloc ();
  60.  
  61. #include "kwset.h"
  62.  
  63. #define NCHAR (UCHAR_MAX + 1)
  64.  
  65. /* For error messages. */
  66. char *program_name;
  67. static int error_seen;
  68.  
  69. /* Flags controlling the style of output. */
  70. static int out_silent;        /* Suppress all normal output. */
  71. static int out_invert;        /* Print nonmatching stuff. */
  72. static int out_file;        /* Print filenames. */
  73. static int out_line;        /* Print line numbers. */
  74. static int out_byte;        /* Print byte offsets. */
  75. static int out_before;        /* Lines of leading context. */
  76. static int out_after;        /* Lines of trailing context. */
  77. #if defined(macintosh) && defined(MPW_OUTPUT)
  78. static int mpw_style;        /* MPW style output */
  79. #endif
  80.  
  81. /* Compiled search pattern. */
  82. kwset_t kwset;
  83.  
  84. /* Flags controlling how pattern matching is performed. */
  85. static int match_fold;        /* Fold all letters to one case. */
  86. static int match_words;        /* Match only whole words. */
  87. static int match_lines;        /* Match only whole lines. */
  88.  
  89. static void
  90. compile (const char *pattern, size_t size)
  91. {
  92.   const char *beg, *lim, *err;
  93.   static char trans[NCHAR];
  94.   int i;
  95.  
  96.   if (match_fold)
  97.     for (i = 0; i < NCHAR; ++i)
  98.       trans[i] = TOLOWER (i);
  99.  
  100.   if (!(kwset = kwsalloc (match_fold ? trans : (const char *) NULL)))
  101.     FATAL ("memory exhausted", 0);
  102.  
  103.   beg = pattern;
  104.   do
  105.     {
  106.       for (lim = beg; lim < pattern + size && *lim != '\n'; ++lim)
  107.     ;
  108.       if (err = kwsincr (kwset, beg, lim - beg))
  109.     FATAL (err, 0);
  110.       if (lim < pattern + size)
  111.     ++lim;
  112.       beg = lim;
  113.     }
  114.   while (beg < pattern + size);
  115.  
  116.   if (err = kwsprep (kwset))
  117.     FATAL (err, 0);
  118. }
  119.  
  120. static char *
  121. execute (char *buf, size_t size)
  122. {
  123.   register char *beg, *try;
  124.   register size_t len;
  125.   struct kwsmatch kwsmatch;
  126.  
  127.   beg = buf;
  128.   for (; beg <= buf + size; ++beg)
  129.     {
  130.       if (!(beg = kwsexec (kwset, beg, buf + size - beg, &kwsmatch)))
  131.     return NULL;;
  132.       len = kwsmatch.size[0];
  133.       if (match_lines)
  134.     {
  135.       if (beg > buf && beg[-1] != '\n')
  136.         continue;
  137.       if (beg + len < buf + size && *(beg + len) != '\n')
  138.         continue;
  139.       return beg;
  140.     }
  141.       else if (match_words)
  142.     for (try = beg; len && try;)
  143.       {
  144.         if (try > buf && (ISALNUM (try[-1])
  145.                   || !ISALNUM (*try)))
  146.           goto retry;
  147.         if (try + len < buf + size
  148.         && (ISALNUM (*(try + len))
  149.             || !ISALNUM ((try + len)[-1])))
  150.           goto retry;
  151.         return try;
  152.       retry:
  153.         if (--len)
  154.           try = kwsexec (kwset, beg, len, &kwsmatch);
  155.         else
  156.           break;
  157.         len = kwsmatch.size[0];
  158.       }
  159.       else
  160.     return beg;
  161.     }
  162.  
  163.   return NULL;
  164. }
  165.  
  166. /* Hairy buffering mechanism to efficiently support all the options. */
  167. static char *bufbeg;        /* Beginning of user-visible portion. */
  168. static char *buflim;        /* Limit of user-visible portion. */
  169. static char *buf;        /* Pointer to base of buffer. */
  170. static size_t bufalloc;        /* Allocated size of buffer. */
  171. static size_t bufcc;        /* Count of characters in buffer. */
  172. static unsigned long int buftotalcc;
  173.  /* Total character count since reset. */
  174. static char *buflast;        /* Pointer after last character printed. */
  175. static int bufgap;        /* Weird flag indicating buflast is a lie. */
  176. static unsigned long int buftotalnl;
  177.  /* Count of newlines before last character. */
  178. static int bufpending;        /* Lines of pending output at buflast. */
  179. static int bufdesc;        /* File descriptor to read from. */
  180. static int bufeof;        /* Flag indicating EOF reached. */
  181. static const char *buffile;    /* File name for messages. */
  182.  
  183. /* Scan and count the newlines prior to LIM in the buffer. */
  184. static void
  185. nlscan (register char *lim)
  186. {
  187.   register char *p;
  188.  
  189.   for (p = buflast; p < lim; ++p)
  190.     if (*p == '\n')
  191.       ++buftotalnl;
  192.   buflast = lim;
  193. }
  194.  
  195. /* Print the line beginning at BEG, using SEP to separate optional label
  196.    fields from the text of the line.  Return the size of the line. */
  197. static size_t
  198. prline (register char *beg, register char sep)
  199. {
  200.   register size_t cc;
  201.   register char c;
  202.   static int err;
  203.  
  204.   cc = 0;
  205.  
  206.   if (out_silent || err)
  207.     while (beg < buflim)
  208.       {
  209.     ++cc;
  210.     if (*beg++ == '\n')
  211.       break;
  212.       }
  213.   else
  214.     {
  215. #if defined(macintosh) && defined(MPW_OUTPUT)
  216.       if (mpw_style)
  217.     {
  218.       if (out_file)
  219.         printf ("File \"%s\"; ", buffile);
  220.       if (out_line)
  221.         {
  222.           nlscan (beg);
  223.           printf ("Line %d ", buftotalnl + 1);
  224.         }
  225.       if (out_byte)
  226.         if (!out_line)
  227.           printf ("Line •!%lu ", buftotalcc + (beg - buf));
  228.         else
  229.           printf ("#%lu ", buftotalcc + (beg - buf));
  230.       printf ("#%c#", sep);
  231.     }
  232.       else
  233. #endif
  234.     {
  235.       if (out_file)
  236.     printf ("%s%c", buffile, sep);
  237.       if (out_line)
  238.     {
  239.       nlscan (beg);
  240.       printf ("%d%c", buftotalnl + 1, sep);
  241.     }
  242.       if (out_byte)
  243.     printf ("%lu%c", buftotalcc + (beg - buf), sep);
  244.     }
  245.     
  246.       while (beg < buflim)
  247.     {
  248.       ++cc;
  249.       c = *beg++;
  250.       putchar (c);
  251.       if (c == '\n')
  252.         break;
  253.     }
  254.       if (ferror (stdout))
  255.     {
  256.       FGREP_ERROR ("output error", errno);
  257.       err = 1;
  258.     }
  259.     }
  260.  
  261.   if (out_line)
  262.     nlscan (beg);
  263.   else
  264.     buflast = beg;
  265.   bufgap = 0;
  266.  
  267.   return cc;
  268. }
  269.  
  270. /* Print pending bytes of last trailing context prior to LIM. */
  271. static void
  272. prpending (register char *lim)
  273. {
  274.   while (buflast < lim && bufpending)
  275.     {
  276.       --bufpending;
  277.       prline (buflast, '-');
  278.     }
  279. }
  280.  
  281. /* Print the lines between BEG and LIM.  Deal with context crap.
  282.    Return the count of lines between BEG and LIM. */
  283. static int
  284. prtext (char *beg, char *lim)
  285. {
  286.   static int used;
  287.   register char *p;
  288.   int i, n;
  289.  
  290.   prpending (beg);
  291.  
  292.   p = beg;
  293.   for (i = 0; i < out_before; ++i)
  294.     if (p > buflast)
  295.       do
  296.     --p;
  297.       while (p > buflast && p[-1] != '\n');
  298.  
  299.   if ((out_before || out_after) && used && (p > buflast || bufgap))
  300. #if defined (macintosh) && defined(MPW_OUTPUT)
  301.     if (mpw_style)
  302.       puts ("##");
  303.     else
  304. #endif
  305.     puts ("--");
  306.  
  307.   while (p < beg)
  308.     p += prline (p, '-');
  309.  
  310.   n = 0;
  311.   while (p < lim)
  312.     {
  313.       ++n;
  314.       p += prline (p, ':');
  315.     }
  316.  
  317.   bufpending = out_after;
  318.   used = 1;
  319.  
  320.   return n;
  321. }
  322.  
  323. /* Fill the user-visible portion of the buffer, returning a byte count. */
  324. static int
  325. fillbuf ()
  326. {
  327.   register char *b, *d, *l;
  328.   int i, cc;
  329.   size_t discard, save;
  330.  
  331.   prpending (buflim);
  332.  
  333.   b = buflim;
  334.   for (i = 0; i < out_before; ++i)
  335.     if (b > buflast)
  336.       do
  337.     --b;
  338.       while (b > buflast && b[-1] != '\n');
  339.  
  340.   if (buflast < b)
  341.     bufgap = 1;
  342.   if (out_line)
  343.     nlscan (b);
  344.  
  345.   discard = b - buf;
  346.   save = buflim - b;
  347.  
  348.   if (b > buf)
  349.     {
  350.       d = buf;
  351.       l = buf + bufcc;
  352.       while (b < l)
  353.     *d++ = *b++;
  354.     }
  355.  
  356.   bufcc -= discard;
  357.   buftotalcc += discard;
  358.  
  359.   do
  360.     {
  361.       if (!bufeof)
  362.     {
  363.       if (bufcc > bufalloc / 2)
  364.         buf = xrealloc (buf, bufalloc *= 2);
  365.       cc = read (bufdesc, buf + bufcc, bufalloc - bufcc);
  366.       if (cc < 0)
  367.         {
  368.           FGREP_ERROR (buffile, errno);
  369.           bufeof = 1;
  370.         }
  371.       else
  372.         {
  373.           bufeof = !cc;
  374.           bufcc += cc;
  375.         }
  376.     }
  377.       bufbeg = buf + save;
  378.       for (l = buf + bufcc; l > bufbeg && l[-1] != '\n'; --l)
  379.     ;
  380.       buflim = l;
  381.       buflast = buf;
  382.     }
  383.   while (!bufeof && bufbeg == buflim);
  384.  
  385.   if (bufeof)
  386.     buflim = buf + bufcc;
  387.  
  388.   return buflim - bufbeg;
  389. }
  390.  
  391. /* One-time initialization. */
  392. static void
  393. initbuf ()
  394. {
  395.   bufalloc = 8192;
  396.   buf = xmalloc (bufalloc);
  397. }
  398.  
  399. /* Reset the buffer for a new file. */
  400. static void
  401. resetbuf (int desc, const char *file)
  402. {
  403.   bufbeg = buf;
  404.   buflim = buf;
  405.   bufcc = 0;
  406.   buftotalcc = 0;
  407.   buflast = buf;
  408.   bufgap = 0;
  409.   buftotalnl = 0;
  410.   bufpending = 0;
  411.   bufdesc = desc;
  412.   bufeof = 0;
  413.   buffile = file;
  414. }
  415.  
  416. /* Scan the user-visible portion of the buffer, calling prtext() for
  417.    matching lines (or between matching lines if OUT_INVERT is true).
  418.    Return a count of lines printed. */
  419. static int
  420. grepbuf ()
  421. {
  422.   int total;
  423.   register char *p, *b, *l;
  424.  
  425.   total = 0;
  426.   p = bufbeg;
  427.   while (b = execute (p, buflim - p))
  428.     {
  429.       if (b == buflim && (b > bufbeg && b[-1] == '\n' || b == bufbeg))
  430.     break;
  431.       while (b > bufbeg && b[-1] != '\n')
  432.     --b;
  433.       l = b + 1;
  434.       while (l < buflim && l[-1] != '\n')
  435.     ++l;
  436.       if (!out_invert)
  437.     total += prtext (b, l);
  438.       else if (p < b)
  439.     total += prtext (p, b);
  440.       p = l;
  441.     }
  442.   if (out_invert && p < buflim)
  443.     total += prtext (p, buflim);
  444.   return total;
  445. }
  446.  
  447. /* Scan the given file, returning a count of lines printed. */
  448. static int
  449. grep (int desc, const char *file)
  450. {
  451.   int total;
  452.  
  453.   total = 0;
  454.   resetbuf (desc, file);
  455.   while (fillbuf ())
  456.     total += grepbuf ();
  457.   return total;
  458. }
  459.  
  460. static const char version[] = "GNU fgrep, version 1.1";
  461.  
  462. #ifndef macintosh
  463. #define USAGE \
  464.   "usage: %s [-[[AB] ]<num>] [-[CVchilnsvwx]] [-[ef]] <expr> [<files...>]\n"
  465. #else
  466. #define USAGE \
  467.   "# Usage - %s [-[[AB] ]num] [-[CVchilmnsvwx]] [-[ef]] expression [file…]\n"
  468. #endif
  469.  
  470. static void
  471. usage (void)
  472. {
  473.   fprintf (stderr, USAGE, program_name);
  474.   exit (2);
  475. }
  476.  
  477. int
  478. main (int argc, char *argv[])
  479. {
  480.   char *keys;
  481.   size_t keycc, keyalloc;
  482.   int count_matches, no_filenames, list_files;
  483.   int opt, cc, desc, count, status;
  484.   FILE *fp;
  485.  
  486.   program_name = argv[0];
  487. #ifndef macintosh
  488.   if (program_name && strrchr (program_name, '/'))
  489.     program_name = strrchr (program_name, '/') + 1;
  490. #else
  491.   if (program_name && strrchr (program_name, ':'))
  492.     program_name = strrchr (program_name, ':') + 1;
  493. #endif
  494.  
  495.   keys = NULL;
  496.   count_matches = 0;
  497.   no_filenames = 0;
  498.   list_files = 0;
  499.  
  500. #if defined(macintosh) && defined(MPW_OUTPUT)
  501.   while ((opt = getopt (argc, argv, "0123456789A:B:CVbce:f:hilmnsvwxy")) != EOF)
  502. #else
  503.   while ((opt = getopt (argc, argv, "0123456789A:B:CVbce:f:hilmnsvwxy")) != EOF)
  504. #endif
  505.     switch (opt)
  506.       {
  507.       case '0':
  508.       case '1':
  509.       case '2':
  510.       case '3':
  511.       case '4':
  512.       case '5':
  513.       case '6':
  514.       case '7':
  515.       case '8':
  516.       case '9':
  517.     out_before = 10 * out_before + opt - '0';
  518.     out_after = 10 * out_after + opt - '0';
  519.     break;
  520.       case 'A':
  521.     out_after = atoi (optarg);
  522.     if (out_after < 0)
  523.       usage ();
  524.     break;
  525.       case 'B':
  526.     out_before = atoi (optarg);
  527.     if (out_before < 0)
  528.       usage ();
  529.     break;
  530.       case 'C':
  531.     out_before = out_after = 2;
  532.     break;
  533.       case 'V':
  534. #ifdef macintosh
  535.     fputs ("# ", stderr);
  536. #endif
  537.     fprintf (stderr, "%s\n", version);
  538.     break;
  539.       case 'b':
  540.     out_byte = 1;
  541.     break;
  542.       case 'c':
  543.     out_silent = 1;
  544.     count_matches = 1;
  545.     break;
  546.       case 'e':
  547.     if (keys)
  548.       usage ();
  549.     keys = optarg;
  550.     keycc = strlen (keys);
  551.     break;
  552.       case 'f':
  553.     if (keys)
  554.       usage ();
  555.     fp = strcmp (optarg, "-") ? fopen (optarg, "r") : stdin;
  556.     if (!fp)
  557.       FATAL (optarg, errno);
  558.     keyalloc = 1024;
  559.     keys = xmalloc (keyalloc);
  560.     keycc = 0;
  561.     while (!feof (fp)
  562.            && (cc = fread (keys + keycc, 1, keyalloc - keycc, fp)) > 0)
  563.       {
  564.         keycc += cc;
  565.         if (keycc == keyalloc)
  566.           keys = xrealloc (keys, keyalloc *= 2);
  567.       }
  568.     if (fp != stdin)
  569.       fclose (fp);
  570.     break;
  571.       case 'h':
  572.     no_filenames = 1;
  573.     break;
  574.       case 'i':
  575.       case 'y':        /* For old-timers . . . */
  576.     match_fold = 1;
  577.     break;
  578.       case 'l':
  579.     out_silent = 1;
  580.     list_files = 1;
  581.     break;
  582. #if defined(macintosh) && defined(MPW_OUTPUT)
  583.       case 'm':
  584.         mpw_style = 1;
  585.     break;
  586. #endif
  587.       case 'n':
  588.     out_line = 1;
  589.     break;
  590.       case 's':
  591.     out_silent = 1;
  592.     break;
  593.       case 'v':
  594.     out_invert = 1;
  595.     break;
  596.       case 'w':
  597.     match_words = 1;
  598.     break;
  599.       case 'x':
  600.     match_lines = 1;
  601.     break;
  602.       default:
  603.     usage ();
  604.     break;
  605.       }
  606.  
  607.   if (!keys)
  608.     if (optind < argc)
  609.       {
  610.     keys = argv[optind++];
  611.     keycc = strlen (keys);
  612.       }
  613.     else
  614.       usage ();
  615.  
  616.   compile (keys, keycc);
  617.  
  618.   if (argc - optind > 1 && !no_filenames)
  619.     out_file = 1;
  620.  
  621.   status = 1;
  622.   initbuf ();
  623.  
  624.   if (optind < argc)
  625.     while (optind < argc)
  626.       {
  627.     desc = strcmp (argv[optind], "-") ? open (argv[optind], 0) : 0;
  628.     if (desc < 0)
  629.       FGREP_ERROR (argv[optind], errno);
  630.     else
  631.       {
  632.         count = grep (desc, argv[optind]);
  633.         if (count_matches)
  634.           {
  635. #if defined(macintosh) && defined(MPW_OUTPUT)
  636.         if (mpw_style)
  637.           {
  638.             if (out_file)
  639.               printf ("File '%s'", argv[optind]);
  640.             printf (" # %d\n", count);
  641.           }
  642.         else
  643. #endif
  644.           {
  645.         if (out_file)
  646.           printf ("%s:", argv[optind]);
  647.         printf ("%d\n", count);
  648.           }
  649.           }
  650.         if (count)
  651.           {
  652.         status = 0;
  653.         if (list_files)
  654. #if defined(macintosh) && defined(MPW_OUTPUT)
  655.           if (mpw_style)
  656.             printf ("File '%s'\n", argv[optind]);
  657.           else
  658. #endif
  659.           printf ("%s\n", argv[optind]);
  660.           }
  661.       }
  662.     if (desc)
  663.       close (desc);
  664.     ++optind;
  665.       }
  666.   else
  667.     {
  668.       count = grep (0, "<stdin>");
  669.       if (count_matches)
  670. #if defined(macintosh) && defined(MPW_OUTPUT)
  671.     if (mpw_style)
  672.       printf ("# %d\n", count);
  673.     else
  674. #endif
  675.     printf ("%d\n", count);
  676.       if (count)
  677.     {
  678.       status = 0;
  679.       if (list_files)
  680. #if defined(macintosh) && defined(MPW_OUTPUT)
  681.         if (mpw_style)
  682.           printf ("File '%s'\n", argv[optind]);
  683.         else
  684. #endif
  685.         printf ("%s\n", argv[optind]);
  686.     }
  687.     }
  688.  
  689.   exit (error_seen ? 2 : status);
  690. }
  691.